#pragma once

/*
    Details.hpp 用于实现一些项目中将用到的小组件
    
*/
#include <iostream>
#include <sstream>
#include <ctime>
#include <string>
#include <unordered_map>
#include <random>
#include <iomanip>
#include <atomic>
#include <memory>
#include <jsoncpp/json/json.h>

namespace rpc
{
    const int Debug = 0;
    const int Info = 1;
    const int Error = 2;
    const int Default = Debug;

    static std::unordered_map<int, std::string> level_hash{
        {Debug, "DEBUG"},
        {Info, "INFO"},
        {Error, "ERROR"}};

#define LOG(level, format, ...)                                                                            \
    {                                                                                                      \
        if (level >= rpc::Default)                                                                              \
        {                                                                                                  \
            time_t t = time(NULL);                                                                         \
            struct tm *lt = localtime(&t);                                                                 \
            char buf[32];                                                                                  \
            size_t n = strftime(buf, 31, "%Y/%m/%d %T", lt);                                               \
            buf[n] = 0;                                                                                    \
            const char *ret = rpc::level_hash[level].c_str();                                                   \
            fprintf(stdout, "[%s][%s][%s:%d]  " format "\n", ret, buf, __FILE__, __LINE__, ##__VA_ARGS__); \
        }                                                                                                  \
    }

#define DLOG(format, ...) LOG(rpc::Debug, format, ##__VA_ARGS__)
#define ILOG(format, ...) LOG(rpc::Info, format, ##__VA_ARGS__)
#define ELOG(format, ...) LOG(rpc::Error, format, ##__VA_ARGS__)

    class JSON
    {
    public:
        // 链接 json 库: -ljsoncpp
        static bool Serialize(Json::Value &value, std::string &body)
        {
            std::stringstream ss;
            Json::StreamWriterBuilder swb;

            // StreamWriter 是一个抽象类，不能直接实例化
            // std::unique_ptr<Json::StreamWriter> 会创建一个 Json::StreamWriter 类的对象
            // std::unique_ptr<Json::StreamWriter> wb = std::unique_ptr<Json::StreamWriter>(swb.newStreamWriter());

            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());

            int n = sw->write(value, &ss);
            if (n != 0)
            {
                std::cout << "serialize failed" << std::endl;
                return false;
            }

            body = ss.str();
            return true;
        }

        static bool Unserialize(Json::Value &value, const std::string &body)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
            std::string error;
            bool ret = cr->parse(body.c_str(), body.c_str() + body.size(), &value, &error);
            if (ret != true)
            {
                std::cout << "unserialize failed : " << error << std::endl;
                return false;
            }
            return true;
        }
    };

    class UUID
    {
    public:
        static std::string uuid()
        {
            std::stringstream ss;

            std::random_device rd;
            std::mt19937 gen(rd());
            std::uniform_int_distribution<int> distribution(0, 255);

            // 生成16个十六进制数
            for (int i = 0; i < 8; i++)
            {
                if (i == 4 || i == 6)
                    ss << "-";
                // setw 设置输出宽度为 2
                // setfill 不足两位则补 0
                ss << std::setw(2) << std::setfill('0') << std::hex << distribution(gen);
            }
            ss << "-";

            static std::atomic<size_t> sq(1); // 修改为静态成员变量
            size_t cur = sq.fetch_add(1);
            for (int i = 7; i >= 0; i--)
            {
                if (i == 5)
                    ss << "-";
                ss << std::setw(2) << std::setfill('0') << std::hex << ((cur >> (i * 8)) & 0xFF);
            }

            return ss.str();
        }
    };
}